using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

using WW.Actions;
using WW.Cad.Drawing;
using WW.Cad.Drawing.GDI;
using WW.Cad.Model;
using WW.Cad.Model.Entities;
using WW.Drawing;
using WW.Math;
using WW.Math.Geometry;
using WW;

namespace StuertzDatenbank
{
    /// <summary>
    /// This is a control that shows a DxfModel.
    /// Dragging with the mouse pans the drawing.
    /// Clicking on the drawing selects the closest entity and
    /// shows it in the property grid.
    /// Using the scroll wheel zooms in on the mouse position.
    /// </summary>
    public partial class ViewControl : UserControl {
        private DxfModel model;
        private GDIGraphics3D gdiGraphics3D;
        private Bounds3D bounds;
        private Matrix4D from2DTransform;
        private Point mouseClickLocation;
        private bool mouseDown;
        private bool shiftPressed;

        #region zooming and panning
        private SimpleTransformationProvider3D transformationProvider;
        private SimplePanInteractor panInteractor;
        private SimpleRectZoomInteractor rectZoomInteractor;
        private SimpleZoomWheelInteractor zoomWheelInteractor;
        private IInteractorWinFormsDrawable rectZoomInteractorDrawable;
        private IInteractorWinFormsDrawable currentInteractorDrawable;
        #endregion

        public event EventHandler<EntityEventArgs> EntitySelected;

        public ViewControl() {
            InitializeComponent();
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.DoubleBuffer, true);
            SetStyle(ControlStyles.UserPaint, true);
            GraphicsConfig graphicsConfig = new GraphicsConfig();
            graphicsConfig.BackColor = BackColor;
            graphicsConfig.CorrectColorForBackgroundColor = true;
            gdiGraphics3D = new GDIGraphics3D(graphicsConfig);
            bounds = new Bounds3D();

            transformationProvider = new SimpleTransformationProvider3D();
            transformationProvider.TransformsChanged += new EventHandler(transformationProvider_TransformsChanged);
            panInteractor = new SimplePanInteractor(transformationProvider);
            rectZoomInteractor = new SimpleRectZoomInteractor(transformationProvider);
            zoomWheelInteractor = new SimpleZoomWheelInteractor(transformationProvider);
            rectZoomInteractorDrawable = new SimpleRectZoomInteractor.WinFormsDrawable(rectZoomInteractor);
        }

        public DxfModel Model {
            get { 
                return model; 
            }
            set { 
                model = value;
                if (model != null) {
                    gdiGraphics3D.CreateDrawables(model);
                    // Uncomment for rotation example.
                    //			transformationProvider.WorldTransform = Transformation4D.RotateX(-30d * Math.PI / 180d) *
                    //				Transformation4D.RotateZ(150d * Math.PI / 180d);
                    gdiGraphics3D.BoundingBox(bounds, transformationProvider.WorldTransform);
                    transformationProvider.ResetTransforms(bounds);
                    CalculateTo2DTransform();
                    Invalidate();
                }
            }
        }

        public Point2D GetModelSpaceCoordinates(Point2D screenSpaceCoordinates) {
            return from2DTransform.TransformTo2D(screenSpaceCoordinates);
        }

        protected override void OnPaint(PaintEventArgs e) {
            Graphics gr = e.Graphics;
            gdiGraphics3D.Draw(e.Graphics, ClientRectangle);
            if (currentInteractorDrawable != null) {
                currentInteractorDrawable.Draw(
                    e, 
                    new InteractorDrawableContext(
                        GetClientRectangle2D(), 
                        transformationProvider.CompleteTransform, 
                        new ArgbColor(BackColor.ToArgb())
                    )
                );
            }
        }

        protected override void OnResize(EventArgs e) {
            base.OnResize(e);
            CalculateTo2DTransform();
            Invalidate();
        }

        protected override void OnMouseDown(MouseEventArgs e) {
            base.OnMouseDown(e);
            mouseClickLocation = e.Location;
            mouseDown = true;
            shiftPressed = ModifierKeys == Keys.Shift;
            if (shiftPressed) {
                rectZoomInteractor.Activate();
                rectZoomInteractor.ProcessMouseButtonDown(new CanonicalMouseEventArgs(e), GetInteractionContext());
                currentInteractorDrawable = rectZoomInteractorDrawable;
            } else {
                panInteractor.Activate();
                panInteractor.ProcessMouseButtonDown(new CanonicalMouseEventArgs(e), GetInteractionContext());
            }
        }

        protected override void OnMouseMove(MouseEventArgs e) {
            base.OnMouseMove(e);
            if (mouseDown == true) {
                if (shiftPressed) {
                    rectZoomInteractor.ProcessMouseMove(new CanonicalMouseEventArgs(e), GetInteractionContext());
                } else {
                    panInteractor.ProcessMouseMove(new CanonicalMouseEventArgs(e), GetInteractionContext());
                }
                Invalidate();
            }
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            mouseDown = false;

            // Use shift key for rectangle zoom.
            if (shiftPressed)
            {
                rectZoomInteractor.ProcessMouseButtonUp(new CanonicalMouseEventArgs(e), GetInteractionContext());
                rectZoomInteractor.Deactivate();
                Invalidate();
            }
            else
            {
                try
                {

                    // Select entity at mouse location if mouse didn't move
                    // and show entity in property grid.
                    if (mouseClickLocation == e.Location)
                    {
                        Point2D referencePoint = new Point2D(e.X, e.Y);
                        double distance;
                        IList<RenderedEntityInfo> closestEntities =
                            EntitySelector.GetClosestEntities(
                                model,
                                GraphicsConfig.BlackBackgroundCorrectForBackColor,
                                gdiGraphics3D.To2DTransform,
                                referencePoint,
                                out distance
                            );
                        if (closestEntities.Count > 0)
                        {
                            DxfEntity entity = closestEntities[0].Entity;
                            OnEntitySelected(new EntityEventArgs(entity));
                        }
                    }

                    currentInteractorDrawable = null;
                    panInteractor.Deactivate();
                }



                catch (Exception)
                {


                }


            }
        }

        protected override void OnMouseWheel(MouseEventArgs e) {
            base.OnMouseWheel(e);

            zoomWheelInteractor.Activate();
            zoomWheelInteractor.ProcessMouseWheel(new CanonicalMouseEventArgs(e), GetInteractionContext());
            zoomWheelInteractor.Deactivate();

            Invalidate();
        }

        protected virtual void OnEntitySelected(EntityEventArgs e) {
            if (EntitySelected != null) {
                EntitySelected(this, e);
            }
        }

        private Matrix4D CalculateTo2DTransform() {
            transformationProvider.ViewWindow = GetClientRectangle2D();
            Matrix4D to2DTransform = Matrix4D.Identity;
            if (model != null && bounds != null) {
                to2DTransform = transformationProvider.CompleteTransform;
            }
            gdiGraphics3D.To2DTransform = to2DTransform;
            from2DTransform = gdiGraphics3D.To2DTransform.GetInverse();
            return to2DTransform;
        }

        private Rectangle2D GetClientRectangle2D() {
            return new Rectangle2D(
                ClientRectangle.Left,
                ClientRectangle.Top,
                ClientRectangle.Right,
                ClientRectangle.Bottom
            );
        }

        private void transformationProvider_TransformsChanged(object sender, EventArgs e) {
            CalculateTo2DTransform();
            Invalidate();
        }

        private InteractionContext GetInteractionContext() {
            return new InteractionContext(
                transformationProvider.CompleteTransform, 
                new Size2D(ClientSize.Width, ClientSize.Height)
            );
        }
    }
}
